<?php
/** 
 * EGP Framework
 *
 * LICENSE
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * @author     Akon(番茄红了) <aultoale@gmail.com>
 * @copyright  Copyright (c) 2008 (http://www.tblog.com.cn)
 * @license    http://www.gnu.org/licenses/gpl.html     GPL 3
 */

!defined('EGP_LIB_DIR') && die('Access Deny!');

/**
 * 财付通支付
 *
 * @package    classes
 * @author     Akon(番茄红了) <aultoale@gmail.com>
 * @copyright  Copyright (c) 2008 (http://www.tblog.com.cn)
 * @license    http://www.gnu.org/licenses/gpl.html     GPL 3
 */
class EGP_Payment_Adapter_Tenpay extends EGP_Payment_Adapter_Abstract
{

    /**
     * 支付方式参数
     *
     * @var array
     */
    protected $_config = array(
        'cmdno'             => 1,    //业务代码, 1:支付接口 2:查询接口
        'date'              => null, //交易日期
        'bank_type'         => 0,    //银行类型: 财付通支付填0
        'desc'              => null, //交易的商品名称, 32个字符16汉字内, 不包含特殊符号
        'purchaser_id'      => null, //用户(买方)的财付通帐户(QQ或EMAIL)
        'bargainor_id'      => null, //商家的商户号
        'transaction_id'    => null, //交易订单号
        'sp_billno'         => null, //商户系统内部定单号
        'total_fee'         => null, //订单总金额
        'fee_type'          => 1,    //现金支付币种
        'return_url'        => null, //支付响应 URL
        'attach'            => null, //商家数据包
        'spbill_create_ip'  => null, //客户端 IP
        'key'               => null, //商户密钥
    );

    /**
     * 构造方法
     *
     * @param  array  $config
     */
    public function __construct(array $config = array())
    {
        if (!isset($config['date'])) {
            $config['date'] = date('Ymd');
        }
        parent::__construct($config);
    }

    /**
     * 设置订单编号
     *
     * @param  string  $id
     * @return EGP_Payment_Adapter_Tenpay
     */
    public function setId($id)
    {
        $id = trim((string) $id);

        if (empty($id)) {
            throw new EGP_Exception('订单编号不能为空');
        }

        if (strlen($id) > 28) {
            throw new EGP_Exception('订单编号的长度不能大于 28');
        }

        $this->_config['sp_billno'] = $id;
        if (strlen($id) > 10) {
            $id = substr($id, -10);
        }
        $this->setTransactionId($id);

        return $this;
    }

    /**
     * 设置订单主题
     *
     * @param  string  $subject
     * @return EGP_Payment_Adapter_Tenpay
     * @throws EGP_Exception
     */
    public function setSubject($subject)
    {
        $subject = str_break(trim(strip_tags($subject)), 32);

        if (empty($subject)) {
            throw new EGP_Exception("商品名称不能为空");
        }

        $this->_config['desc'] = $subject;

        return $this;
    }

    /**
     * 设置订单总金额
     *
     * @param  float  $amount
     * @return EGP_Payment_Adapter_Tenpay
     */
    public function setAmount($amount)
    {
        if (empty($amount)) {
            throw new EGP_Exception('订单总金额不能为空');
        }

        if (!$this->_isFloat($amount) || floatval($amount) <= 0) {
            throw new EGP_Exception('无效的订单总金额');
        }

        $this->_config['total_fee'] = $amount;

        return $this;
    }

    /**
     * 设置业务代码
     *
     * 1: 支付接口 2: 查询接口
     *
     * @param  integer  $cmdno
     * @return EGP_Payment_Adapter_Tenpay
     * @throws EGP_Exception
     */
    public function setCmdno($no)
    {
        if ($no != 1 && $no != 2) {
            throw new EGP_Exception('错误的业务代码');
        }

        $this->_config['cmdno'] = (integer) $no;

        return $this;
    }

    /**
     * 设置交易日期
     *
     * 格式 date('Ymd')
     *
     * @param  string  $date
     * @return EGP_Payment_Adapter_Tenpay
     * @throws EGP_Exception
     */
    public function setDate($date)
    {
        if (preg_match('/[\d]{6}/', $date)) {
            $year = substr($date, 0, 4);
            $month = substr($date, 4, 2);
            $day = substr($date, 6, 2);
            if (checkdate($month, $day, $year)) {
                $this->_config['date'] = $date;
                return $this;
            }
        }
        throw new EGP_Exception("无效的交易日期格式 '$date'");
    }

    /**
     * 设置银行类型
     *
     * @param  integer  $type
     * @return EGP_Payment_Adapter_Tenpay
     * @throws EGP_Exception
     */
    public function setBankType($type)
    {
        if (!array_key_exists($type, $this->getBankTypes())) {
            throw new EGP_Exception('不支持的银行类型');
        }

        $this->_config['bank_type'] = (string) $type;

        return $this;
    }

    /**
     * 设置交易的商品名称
     *
     * 32 个字符 16 汉字内, 不包含特殊符号, 超出部分自动截取
     *
     * @param  string  $desc
     * @return EGP_Payment_Adapter_Tenpay
     * @throws EGP_Exception
     */
    public function setDesc($desc)
    {
        return $this->setSubject($desc);
    }

    /**
     * 设置用户(买方)的财付通帐户
     *
     * QQ 或 EMAIL
     *
     * @param  string  $id
     * @return EGP_Payment_Adapter_Tenpay
     * @throws EGP_Exception
     */
    public function setPurchaserId($id)
    {
        $id = trim((string) $id);

        if (!empty($id)) {
            if (preg_match('/^[1-9]\d{4,12}$/', $id) || $this->_isMail($id)) {
                $this->_config['purchaser_id'] = $id;
            } else {
                throw new EGP_Exception("无效的用户财付通帐户");
            }
        }

        return $this;
    }

    /**
     * 设定商家的商户号
     *
     * @param  string  $id
     * @return EGP_Payment_Adapter_Tenpay
     * @throws EGP_Exception
     */
    public function setBargainorId($id)
    {
        $id = trim((string) $id);

        if (empty($id)) {
            throw new EGP_Exception("商家商户号不能为空");
        }

        if (preg_match('/[^\d]+/', $id)) {
            throw new EGP_Exception("无效的商家商户号");
        }

        $this->_config['bargainor_id'] = $id;

        return $this;
    }

    /**
     * 交易订单号
     *
     * transaction_id 为28位长的数值，其中前10位为商户网站编号(SPID)，由财付通统一分配；
     * 之后8位为订单产生的日期，如20050415；
     * 最后10位商户需要保证一天内不同的事务（用户订购一次商品或购买一次服务），其ID不相同。
     * 此财付通订单号必须保持唯一，不能重复,财付通根据此定单号通知商户发货和数据更新等。
     *
     * @param  string  $id
     * @return EGP_Payment_Adapter_Tenpay
     * @throws EGP_Exception
     */
    public function setTransactionId($id)
    {
        $prefix = $this->_config['bargainor_id'] . $this->_config['date'];
        $id = str_replace($prefix, '', trim((string) $id));

        if (empty($id)) {
            throw new EGP_Exception('交易订单号不能为空');
        }

        if (preg_match('/[^\d]+/', $id)) {
            throw new EGP_Exception('无效的交易订单号');
        }

        if (strlen($id) > 10) {
            throw new EGP_Exception('交易订单号的长度不能大于 10');
        }

        $this->_config['transaction_id'] = $prefix .str_pad($id, 10, 0, STR_PAD_LEFT);

        return $this;
    }

    /**
     * 设定商户系统内部定单号
     *
     * 此参数仅在对账时提供, 28 个字符内
     *
     * @param  string  $no
     * @return EGP_Payment_Adapter_Tenpay
     * @throws EGP_Exception
     */
    public function setSpBillno($no)
    {
        return $this->setId($no);
    }

    /**
     * 设定订单总金额
     *
     * 以分为单位, 浮点类型
     *
     * @param  float  $fee
     * @return EGP_Payment_Adapter_Tenpay
     * @throws EGP_Exception
     */
    public function setTotalFee($fee)
    {
        return $this->setAmount($fee);
    }

    /**
     * 现金支付币种
     *
     * 目前只支持人民币
     *
     * @param  string  $type
     * @return EGP_Payment_Adapter_Tenpay
     * @throws EGP_Exception
     */
    public function setFeeType($type)
    {
        if (!array_key_exists($type, $this->getFeeTypes())) {
            throw new EGP_Exception('不支持的现金支付币种类型');
        }

        $this->_config['fee_type'] = (string) $type;

        return $this;
    }

    /**
     * 设置支付响应 URL
     *
     * 推荐使用 IP 地址
     *
     * @param  string  $url
     * @return EGP_Payment_Adapter_Tenpay
     * @throws EGP_Exception
     */
    public function setReturnUrl($url)
    {
        if (!empty($url) && !$this->_isUrl($url)) {
            throw new EGP_Exception('无效的支付响应 URL');
        }

        $this->_config['return_url'] = (string) $url;

        return $this;
    }

    /**
     * 设定商家数据包
     *
     * 数据包将按原样返回
     *
     * @param  string  $attach
     * @return EGP_Payment_Adapter_Tenpay
     * @throws EGP_Exception
     */
    public function setAttach($attach)
    {
        if (empty($attach)) {
            throw new EGP_Exception('商家数据包不能为空');
        }

        $this->_config['attach'] = (string) $attach;

        return $this;
    }

    /**
     * 设定客户端 IP 地址
     *
     * 为了防止欺诈, 支付时财付通会校验此 IP
     *
     * @param  string  $ip
     * @return EGP_Payment_Adapter_Tenpay
     * @throws EGP_Exception
     */
    public function setSpbillCreateIp($ip)
    {
        if (!empty($ip)) {
            if (!$this->_isIp($ip)) {
                throw new EGP_Exception('无效的客户端 IP 地址');
            }

            $this->_config['spbill_create_ip'] = (string) $ip;
        }

        return $this;
    }

    /**
     * 设置商户密钥
     *
     * @param  string  $key
     */
    public function setKey($key)
    {
        if (empty($key)) {
            throw new EGP_Exception('商户密钥不能为空');
        }

        if (!is_string($key) || !preg_match('/[a-z0-9]{32}/i', $key)) {
            throw new EGP_Exception('无效的商户密钥');
        }

        $this->_config['key'] = (string) $key;

        return $this;
    }

    /**
     * 获取数字签名
     *
     * @return string
     */
    public function getSign()
    {
        extract($this->_config);
        $desc     = iconv('utf-8', 'gbk', $desc);
        $total_fee = $total_fee * 100;
        $signText = "cmdno=$cmdno&date=$date&bargainor_id=$bargainor_id&" .
                    "transaction_id=$transaction_id&sp_billno=$sp_billno&" .
                    "total_fee=$total_fee&fee_type=$fee_type&" .
                    "return_url=$return_url&attach=$attach";
        if (!empty($spbill_create_ip)) {
            $signText .= "&spbill_create_ip=$spbill_create_ip";
        }
        return strtoupper(md5("$signText&key=$key"));
    }

    /**
     * 获取支付网关地址
     *
     * @return string
     */
    public function getGateWay()
    {
        return 'https://www.tenpay.com/cgi-bin/v1.0/pay_gate.cgi';
    }

    /**
     * 获取支付 URL
     *
     * @return string
     */
    public function getPayUrl()
    {
        extract($this->_config);
        $desc         = urlencode(iconv('utf-8', 'gbk', $desc));
        $total_fee = $total_fee * 100;
        $purchaser_id = urlencode($purchaser_id);
        $return_url   = urlencode($return_url);
        $attach       = urlencode($attach);
        $query  = "cmdno=$cmdno&date=$date&bank_type=$bank_type&desc=$desc&" .
                  "purchaser_id=$purchaser_id&bargainor_id=$bargainor_id&" .
                  "transaction_id=$transaction_id&sp_billno=$sp_billno&total_fee=$total_fee&" .
                  "fee_type=$fee_type&return_url=$return_url&attach=$attach";
        if (!empty($spbill_create_ip)) {
            $query .= "&spbill_create_ip=$spbill_create_ip";
        }
        return $this->getGateWay() . '?' . $query . '&sign=' . $this->getSign();
    }

    /**
     * 获取支持的银行代码
     *
     * @return array
     */
    public function getBankTypes()
    {
        return array(
            '0'    => '财付通支付',
            '1001' => '招商银行',
            '1002' => '中国工商银行',
            '1003' => '中国建设银行',
            '1004' => '上海浦东发展银行',
            '1005' => '中国农业银行',
            '1006' => '中国民生银行',
            '1008' => '深圳发展银行',
            '1009' => '兴业银行',
            '1020' => '广州银联',
            '1022' => '北京银行',
            '1028' => '中国交通银行',
            '1032' => '中国光大银行',
        );
    }

    /**
     * 获取支持的现金支付币种
     *
     * @return array
     */
    public function getFeeTypes()
    {
        return array(
            '1' => 'RMB', //人民币
            //'2' => 'USD', //美元
            //'3' => 'HKD', //港币
        );
    }
}